﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Reflection;

namespace System.Windows.Reflection
{
    /// <summary>
    /// Implements a sorting helper class which is used by the <see cref="ArrangeSorter"/>.
    /// </summary>
    public class ArrangeSorter
    {
        /// <summary>
        /// Initializes an <see cref="ArrangeSorter"/> instance.
        /// </summary>
        /// <param name="field">The field or property on the data source to sort against</param>
        /// <param name="direction">The direction of the sort</param>
        public ArrangeSorter(string field, SortDirections direction)
        {
            this.SortField = field;
            this.SortDirection = direction;
        }

        /// <summary>
        /// Gets/Sets the name of the field or property on the data source to sort against
        /// </summary>
        public string           SortField { get; set; }

        /// <summary>
        /// The direction of the sort
        /// </summary>
        public SortDirections   SortDirection { get; set; }

        /// <summary>
        /// A static functor for retrieving a <see cref="System.Double"/> value from a data context object
        /// </summary>
        /// <param name="fieldName">The field or property name to examine</param>
        /// <param name="context">The source object from which to infer the value</param>
        /// <returns>The value requested</returns>
        public static double GetDoubleSortValue(string fieldName, object context)
        {
            object val = GetSortValue(fieldName, context);
            if (val is double) return (double)val;
            return 0.0;
        }

        /// <summary>
        /// A static functor for retrieving a <see cref="System.Double"/> value from a data context object
        /// </summary>
        /// <param name="fieldName">The field or property name to examine</param>
        /// <param name="context">The source object from which to infer the value</param>
        /// <returns>The value requested as an object</returns>
        public static object GetSortValue(string fieldName, object context)
        {
            object val = null;

            PropertyInfo property = context.GetType().GetProperty(fieldName, BindingFlags.Public|BindingFlags.Instance|BindingFlags.GetProperty);
            if (property != null)
            {
                val = property.GetValue(context, null);
            }
            else
            {
                FieldInfo field = context.GetType().GetField(fieldName, BindingFlags.Public|BindingFlags.Instance|BindingFlags.GetField);
                if (field != null)
                {
                    val = field.GetValue(context);
                }
            }

            return val;
        }

        /// <summary>
        /// A static functor for retrieving a <see cref="System.Double"/> value as a proportional value within a prescribed range
        /// </summary>
        /// <remarks>
        /// NOTE: This method will return zero (0) if the types of each parameter are not identifical.
        /// This method also only supports integral values and <see cref="System.DateTime"/> structures.
        /// </remarks>
        /// <param name="val">The value to be evaluated within a range</param>
        /// <param name="min">The minimum for the range</param>
        /// <param name="max">The maximum for the range</param>
        /// <returns>The value requested</returns>
        public static double GetProportionalValue(object val, object min, object max)
        {
            double dblVal = 0;

            if (val is DateTime && min is DateTime && max is DateTime)
            {
                dblVal = GetPropertionalDateTimeValue((DateTime)val, (DateTime)min, (DateTime)max);
            }
            else if (val is double && min is double && max is double)
            {
                dblVal = GetPropertionalDoubleValue((double)val, (double)min, (double)max);
            }
            else if ((val is int && min is int && max is int) ||
                (val is uint && min is uint && max is uint))
            {
                dblVal = GetPropertionalDoubleValue((int)val, (int)min, (int)max);
            }
            else if ((val is long && min is long && max is long) ||
                (val is ulong && min is ulong && max is ulong))
            {
                dblVal = GetPropertionalDoubleValue((int)val, (int)min, (int)max);
            }

            return dblVal;
        }

        private static double GetPropertionalDateTimeValue(DateTime val, DateTime min, DateTime max)
        {
            double range = Math.Abs((max - min).Ticks);
            double valRange = Math.Abs((val - (min > max ? max : min)).Ticks);
            double dblVal = (range > 0 ? valRange / range : 0);
            return dblVal;
        }

        private static double GetPropertionalDoubleValue(double val, double min, double max)
        {
            double range = Math.Abs(max - min);
            double dblVal = (range > 0 ? val / range : 0);
            return dblVal;
        }

        private static double GetPropertionalIntValue(int val, int min, int max)
        {
            double range = Math.Abs(max - min);
            double dblVal = (range > 0 ? val / range : 0);
            return dblVal;
        }

        private static double GetPropertionalLongValue(long val, long min, long max)
        {
            double range = Math.Abs(max - min);
            double dblVal = (range > 0 ? val / range : 0);
            return dblVal;
        }
    }
}
